﻿ #region Disclaimer/Info
 
 /////////////////////////////////////////////////////////////////////////////////////////////////
 //
 //   File:		MembershipProvider.cs
 //   Website:		http://dexterblogengine.com/
 //   Authors:		http://dexterblogengine.com/About.ashx
 //   Rev:		1
 //   Created:		19/01/2011
 //   Last edit:		19/01/2011
 //   License:		GNU Library General Public License (LGPL)
 // 
 //   For updated news and information please visit http://dexterblogengine.com/
 //   Dexter is hosted to Codeplex at http://dexterblogengine.codeplex.com
 //   For any question contact info@dexterblogengine.com
 //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #endregion

using System;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Net.Mail;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Web.Security;
using Dexter.Dependency;
using Dexter.Domain.Model;
using Dexter.Domain.Results;
using Dexter.Logger.Contracts;
using Dexter.Services;

namespace Dexter.Web.Security {
	/// <summary>
	/// 	Custom membership provider base on Deter <see cref = "IUserService" />. See also <seealso cref = "System.Web.Security.MembershipProvider" />.
	/// </summary>
	public class MembershipProvider : System.Web.Security.MembershipProvider{
		/// <summary>
		/// 	The logger.
		/// </summary>
		protected readonly ILogger Logger;

		/// <summary>
		/// 	An instance of a concrete implementation of <see cref = "IUserService" />
		/// </summary>
		protected readonly IUserService UserService;

		bool enablePasswordReset;
		bool enablePasswordRetrieval;
		MachineKeySection machineKey;
		int maxInvalidPasswordAttempts;
		int minRequiredNonAlphanumericCharacters;
		int minRequiredPasswordLength;
		int passwordAttemptWindow;
		MembershipPasswordFormat passwordFormat;
		string passwordStrengthRegularExpression;
		bool requiresQuestionAndAnswer;
		bool requiresUniqueEmail;

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "MembershipProvider" /> class.
		/// </summary>
		public MembershipProvider ( ) : this ( DexterContainer.Resolve <ILogger> ( ) , DexterContainer.Resolve <IUserService> ( ) ) {}

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "MembershipProvider" /> class.
		/// </summary>
		/// <param name = "logger">The logger.</param>
		/// <param name = "userService">The user service.</param>
		public MembershipProvider ( ILogger logger , IUserService userService ) {
			this.Logger = logger;
			this.UserService = userService;
		}

		/// <summary>
		/// 	The name of the application using the custom membership provider.
		/// </summary>
		/// <value></value>
		/// <returns>The name of the application using the custom membership provider.</returns>
		public override string ApplicationName {
			get { return "Dexter"; }
			set { }
		}

		/// <summary>
		/// 	Indicates whether the membership provider is configured to allow users to retrieve their passwords.
		/// </summary>
		/// <value></value>
		/// <returns>true if the membership provider is configured to support password retrieval; otherwise, false. The default is false.</returns>
		public override bool EnablePasswordRetrieval {
			get { return enablePasswordRetrieval; }
		}

		/// <summary>
		/// 	Gets the number of invalid password or password-answer attempts allowed before the membership user is locked out.
		/// </summary>
		/// <value></value>
		/// <returns>The number of invalid password or password-answer attempts allowed before the membership user is locked out.</returns>
		public override int MaxInvalidPasswordAttempts {
			get { return maxInvalidPasswordAttempts; }
		}

		/// <summary>
		/// 	Gets the minimum number of special characters that must be present in a valid password.
		/// </summary>
		/// <value></value>
		/// <returns>The minimum number of special characters that must be present in a valid password.</returns>
		public override int MinRequiredNonAlphanumericCharacters {
			get { return minRequiredNonAlphanumericCharacters; }
		}

		/// <summary>
		/// 	Gets the minimum length required for a password.
		/// </summary>
		/// <value></value>
		/// <returns>The minimum length required for a password. </returns>
		public override int MinRequiredPasswordLength {
			get { return minRequiredPasswordLength; }
		}

		/// <summary>
		/// 	Gets the number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out.
		/// </summary>
		/// <value></value>
		/// <returns>The number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out.</returns>
		public override int PasswordAttemptWindow {
			get { return passwordAttemptWindow; }
		}

		/// <summary>
		/// 	Gets a value indicating the format for storing passwords in the membership data store.
		/// </summary>
		/// <value></value>
		/// <returns>One of the <see cref = "T:System.Web.Security.MembershipPasswordFormat" /> values indicating the format for storing passwords in the data store.</returns>
		public override MembershipPasswordFormat PasswordFormat {
			get { return passwordFormat; }
		}

		/// <summary>
		/// 	Gets the regular expression used to evaluate a password.
		/// </summary>
		/// <value></value>
		/// <returns>A regular expression used to evaluate a password.</returns>
		public override string PasswordStrengthRegularExpression {
			get { return passwordStrengthRegularExpression; }
		}

		/// <summary>
		/// 	Gets a value indicating whether the membership provider is configured to require the user to answer a password question for password reset and retrieval.
		/// </summary>
		/// <value></value>
		/// <returns>true if a password answer is required for password reset and retrieval; otherwise, false. The default is true.</returns>
		public override bool RequiresQuestionAndAnswer {
			get { return requiresQuestionAndAnswer; }
		}

		/// <summary>
		/// 	Gets a value indicating whether the membership provider is configured to require a unique e-mail address for each user name.
		/// </summary>
		/// <value></value>
		/// <returns>true if the membership provider requires a unique e-mail address; otherwise, false. The default is true.</returns>
		public override bool RequiresUniqueEmail {
			get { return requiresUniqueEmail; }
		}

		/// <summary>
		/// 	Indicates whether the membership provider is configured to allow users to reset their passwords.
		/// </summary>
		/// <value></value>
		/// <returns>true if the membership provider supports password reset; otherwise, false. The default is true.</returns>
		public override bool EnablePasswordReset {
			get { return enablePasswordReset; }
		}

		/// <summary>
		/// 	Initializes the provider.
		/// </summary>
		/// <param name = "name">The friendly name of the provider.</param>
		/// <param name = "config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
		/// <exception cref = "T:System.ArgumentNullException">The name of the provider is null.</exception>
		/// <exception cref = "T:System.ArgumentException">The name of the provider has a length of zero.</exception>
		/// <exception cref = "T:System.InvalidOperationException">An attempt is made to call <see cref = "M:System.Configuration.Provider.ProviderBase.Initialize(System.String,System.Collections.Specialized.NameValueCollection)" /> on a provider after the provider has already been initialized.</exception>
		public override void Initialize ( string name , NameValueCollection config ) {
			if ( config == null ) {
				throw new ArgumentNullException ( "config" );
			}

			if ( string.IsNullOrEmpty ( name ) ) {
				name = "DexterMembershipProvider";
			}

			if ( string.IsNullOrEmpty ( config[ "description" ] ) ) {
				config.Remove ( "description" );
				config.Add ( "description" , "Dexter Membership Provider" );
			}
			base.Initialize ( name , config );

			requiresQuestionAndAnswer = Convert.ToBoolean ( config[ "requiresQuestionAndAnswer" ] ?? "False" );
			requiresUniqueEmail = Convert.ToBoolean ( config[ "requiresUniqueEmail" ] ?? "True" );
			enablePasswordRetrieval = Convert.ToBoolean ( config[ "enablePasswordRetrieval" ] ?? "True" );
			enablePasswordReset = Convert.ToBoolean ( config[ "enablePasswordReset" ] ?? "True" );
			maxInvalidPasswordAttempts = Convert.ToInt32 ( config[ "maxInvalidPasswordAttempts" ] ?? "5" );
			passwordAttemptWindow = Convert.ToInt32 ( config[ "passwordAttemptWindow" ] ?? "10" );
			minRequiredPasswordLength = Convert.ToInt32 ( config[ "minRequiredPasswordLength" ] ?? "7" );
			minRequiredNonAlphanumericCharacters = Convert.ToInt32 ( config[ "minRequiredAlphaNumericCharacters" ] ?? "1" );
			passwordStrengthRegularExpression = Convert.ToString ( config[ "passwordStrengthRegularExpression" ] ?? string.Empty );

			string configValue = config[ "passwordFormat" ] ?? "Hashed";


			if ( configValue == "Encrypted" ) {
				passwordFormat = MembershipPasswordFormat.Encrypted;
			}
			if ( configValue == "Clear" ) {
				passwordFormat = MembershipPasswordFormat.Clear;
			}
			if ( configValue == "Hashed" ) {
				passwordFormat = MembershipPasswordFormat.Hashed;
			}

			System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration ( HostingEnvironment.ApplicationVirtualPath );
			machineKey = ( MachineKeySection ) configuration.GetSection ( "system.web/machineKey" );
			if ( "Auto".Equals ( machineKey.Decryption ) ) {
				machineKey.DecryptionKey = SecurityProviderHelper.CreateKey ( 0x18 );
				machineKey.ValidationKey = SecurityProviderHelper.CreateKey ( 0x40 );
			}
		}

		/// <summary>
		/// 	Adds a new membership user to the data source.
		/// </summary>
		/// <param name = "username">The user name for the new user.</param>
		/// <param name = "password">The password for the new user.</param>
		/// <param name = "email">The e-mail address for the new user.</param>
		/// <param name = "passwordQuestion">The password question for the new user.</param>
		/// <param name = "passwordAnswer">The password answer for the new user</param>
		/// <param name = "isApproved">Whether or not the new user is approved to be validated.</param>
		/// <param name = "providerUserKey">The unique identifier from the membership data source for the user.</param>
		/// <param name = "status">A <see cref = "T:System.Web.Security.MembershipCreateStatus" /> enumeration value indicating whether the user was created successfully.</param>
		/// <returns>
		/// 	A <see cref = "T:System.Web.Security.MembershipUser" /> object populated with the information for the newly created user.
		/// </returns>
		public override MembershipUser CreateUser ( string username , string password , string email , string passwordQuestion , string passwordAnswer , bool isApproved , object providerUserKey , out MembershipCreateStatus status ) {
			var e = new ValidatePasswordEventArgs ( username , password , true );
			OnValidatingPassword ( e );

			if ( e.Cancel ) {
				status = MembershipCreateStatus.InvalidPassword;
				return null;
			}

			User user = UserService.GetUserByEmail ( new MailAddress ( email ) );

			if ( RequiresUniqueEmail && user != null ) {
				status = MembershipCreateStatus.DuplicateEmail;
				return null;
			}

			if ( UserService.GetUserByUsername ( username ) != null ) {
				status = MembershipCreateStatus.DuplicateUserName;
				return null;
			}

			try {
				EncryptDataFormat psw;

				switch ( PasswordFormat ) {
					case MembershipPasswordFormat.Clear:
						psw = EncryptDataFormat.Clear;
						break;
					case MembershipPasswordFormat.Encrypted:
						psw = EncryptDataFormat.Encrypted;
						break;
					case MembershipPasswordFormat.Hashed:
						psw = EncryptDataFormat.Hashed;
						break;
					default:
						psw = EncryptDataFormat.Hashed;
						break;
				}

				User usr = User.CreateNewUser ( username , password , psw , HashProvider.SHA1 );
				usr.Email = email;

				if (!string.IsNullOrEmpty ( passwordQuestion ) && !String.IsNullOrEmpty ( passwordAnswer ))
					usr.ChangePasswordQuestionAndAnswer ( passwordQuestion , passwordAnswer );

				if(isApproved)
					usr.Approve ( );
				else {
					usr.Unapprove (  );
				}

				UserService.Save ( usr );

				status = MembershipCreateStatus.Success;
			}
			catch ( Exception ex ) {
				throw new ProviderException ( "Unable to create the User." , ex );
			}

			return GetUser ( username , false );
		}

		/// <summary>
		/// 	Processes a request to update the password question and answer for a membership user.
		/// </summary>
		/// <param name = "username">The user to change the password question and answer for.</param>
		/// <param name = "password">The password for the specified user.</param>
		/// <param name = "newPasswordQuestion">The new password question for the specified user.</param>
		/// <param name = "newPasswordAnswer">The new password answer for the specified user.</param>
		/// <returns>
		/// 	true if the password question and answer are updated successfully; otherwise, false.
		/// </returns>
		public override bool ChangePasswordQuestionAndAnswer ( string username , string password , string newPasswordQuestion , string newPasswordAnswer ) {
			bool flag = false;

			if ( ValidateUser ( username , password ) ) {
				User user = UserService.GetUserByUsername ( username );

				if ( user == null ) {
					return false;
				}

				try {
					user.ChangePasswordQuestionAndAnswer ( newPasswordQuestion , newPasswordAnswer );
					UserService.Update ( user );
					flag = true;
				}
				catch ( Exception ex ) {
					throw new ProviderException ( "Unable to change password question and password answer" , ex );
				}
			}
			return flag;
		}

		/// <summary>
		/// 	Gets the password for the specified user name from the data source.
		/// </summary>
		/// <param name = "username">The user to retrieve the password for.</param>
		/// <param name = "answer">The password answer for the user.</param>
		/// <returns>
		/// 	The password for the specified user name.
		/// </returns>
		public override string GetPassword ( string username , string answer ) {
			string password = null;

			if ( !EnablePasswordRetrieval ) {
				throw new ProviderException ( "Password Retrieval not enabled." );
			}

			if ( MembershipPasswordFormat.Hashed == PasswordFormat ) {
				throw new ProviderException ( "Cannot Retrieve Hashed" );
			}

			User user = UserService.GetUserByUsername ( username );
			if ( user != null ) {
				if ( RequiresQuestionAndAnswer && !user.VerifyPasswordAnswer ( answer ) ) {
					user.UpdateFailurePasswordAswer ( MaxInvalidPasswordAttempts , PasswordAttemptWindow );
					UserService.Update ( user );

					throw new ProviderException ( "Incorrect password answer." );
				}

				if ( MembershipPasswordFormat.Encrypted == PasswordFormat ) {
					password = user.UnencodePassword ( );
				}
			}
			return password;
		}

		/// <summary>
		/// 	Processes a request to update the password for a membership user.
		/// </summary>
		/// <param name = "username">The user to update the password for.</param>
		/// <param name = "oldPassword">The current password for the specified user.</param>
		/// <param name = "newPassword">The new password for the specified user.</param>
		/// <returns>
		/// 	true if the password was updated successfully; otherwise, false.
		/// </returns>
		public override bool ChangePassword ( string username , string oldPassword , string newPassword ) {
			bool flag = false;
			if ( ValidateUser ( username , oldPassword ) ) {
				var e = new ValidatePasswordEventArgs ( username , newPassword , true );
				OnValidatingPassword ( e );
				if ( e.Cancel ) {
					if ( e.FailureInformation != null ) {
						throw e.FailureInformation;
					}

					throw new ProviderException ( "Password change cancelled due to new password" );
				}
				User user = UserService.GetUserByUsername ( username );

				if ( user == null ) {
					return false;
				}

				try {
					user.ChangePassword ( newPassword );
					flag = true;
				}
				catch ( Exception exception ) {
					throw new MembershipPasswordException ( "Password operation cancelled due to account locked" , exception );
				}
			}
			return flag;
		}

		/// <summary>
		/// 	Resets a user's password to a new, automatically generated password.
		/// </summary>
		/// <param name = "username">The user to reset the password for.</param>
		/// <param name = "answer">The password answer for the specified user.</param>
		/// <returns>The new password for the specified user.</returns>
		public override string ResetPassword ( string username , string answer ) {
			if ( !EnablePasswordReset ) {
				throw new MembershipPasswordException ( "Password reset not enabled" );
			}

			User user = UserService.GetUserByUsername ( username );

			if ( ( answer == null ) && RequiresQuestionAndAnswer ) {
				user.UpdateFailurePassword ( PasswordAttemptWindow , MaxInvalidPasswordAttempts );
				throw new MembershipPasswordException ( "Password answer is required for reset" );
			}

			string password = Membership.GeneratePassword ( minRequiredPasswordLength , MinRequiredNonAlphanumericCharacters );
			var e = new ValidatePasswordEventArgs ( username , password , true );
			OnValidatingPassword ( e );

			if ( e.Cancel ) {
				if ( e.FailureInformation != null ) {
					throw e.FailureInformation;
				}

				throw new MembershipPasswordException ( "Password reset cancelled due to new password" );
			}

			if ( user != null ) {
				if ( user.IsLockedOut ) {
					throw new MembershipPasswordException ( "The user is locked out" );
				}

				if ( RequiresQuestionAndAnswer && !user.VerifyPasswordAnswer ( answer ) ) {
					user.UpdateFailurePasswordAswer ( PasswordAttemptWindow , MaxInvalidPasswordAttempts );
					throw new MembershipPasswordException ( "Incorrect password answer" );
				}
				try {
					user.ChangePassword ( password );
					UserService.Update ( user );
				}
				catch {
					throw new MembershipPasswordException ( "Operation cancelled due to account locked" );
				}
			}
			return password;
		}

		/// <summary>
		/// 	Updates information about a user in the data source.
		/// </summary>
		/// <param name = "user">A <see cref = "T:System.Web.Security.MembershipUser" /> object that represents the user to update and the updated information for the user.</param>
		public override void UpdateUser ( MembershipUser user ) {
			try {
				User usr = UserService.GetUserByUsername ( user.UserName );

				if ( usr == null ) {
					throw new ProviderException ( "Unable to retrive the user." );
				}

				UserService.Update ( SecurityProviderHelper.FromMembershipUser ( user , usr ) );
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable To Update the user" , exception );
			}
		}

		/// <summary>
		/// 	Verifies that the specified user name and password exist in the data source.
		/// </summary>
		/// <param name = "username">The name of the user to validate.</param>
		/// <param name = "password">The password for the specified user.</param>
		/// <returns>
		/// 	true if the specified username and password are valid; otherwise, false.
		/// </returns>
		public override bool ValidateUser ( string username , string password ) {
			bool flag = false;
			User user = UserService.GetUserByUsername ( username );
			if ( user != null ) {
				if ( user.VerifyPassword ( password ) ) {
					if ( user.IsApproved ) {
						flag = true;
						user.LastLoginDate = DateTime.Now;
						UserService.Update ( user );
					}
					return flag;
				}
				user.UpdateFailurePassword ( PasswordAttemptWindow , MaxInvalidPasswordAttempts );
				UserService.Update ( user );
			}

			Logger.DebugFormat ( "The user with the username '{0}' is null" , username );
			return false;
		}

		/// <summary>
		/// 	Clears a lock so that the membership user can be validated.
		/// </summary>
		/// <param name = "userName">The membership user whose lock status you want to clear.</param>
		/// <returns>
		/// 	true if the membership user was successfully unlocked; otherwise, false.
		/// </returns>
		public override bool UnlockUser ( string userName ) {
			bool flag = false;
			try {
				User user = UserService.GetUserByUsername ( userName );
				if ( user != null ) {
					user.Unlock ( );
					UserService.Update ( user );
					flag = true;
				}
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable to unlock the user" , exception );
			}
			return flag;
		}

		/// <summary>
		/// 	Gets user information from the data source based on the unique identifier for the membership user. Provides an option to update the last-activity date/time stamp for the user.
		/// </summary>
		/// <param name = "providerUserKey">The unique identifier for the membership user to get information for.</param>
		/// <param name = "userIsOnline">true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user.</param>
		/// <returns>
		/// 	A <see cref = "T:System.Web.Security.MembershipUser" /> object populated with the specified user's information from the data source.
		/// </returns>
		public override MembershipUser GetUser ( object providerUserKey , bool userIsOnline ) {
			MembershipUser user = null;
			User usr;

			if ( providerUserKey == null ) {
				throw new ArgumentNullException ( "providerUserKey" );
			}

			try {
				usr = UserService.GetUserByKey ( ( int ) providerUserKey );

				if ( usr != null ) {
					user = SecurityProviderHelper.ToMembershipUser ( Name , usr );
				}
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable to retrive the user." , exception );
			}

			if ( userIsOnline ) {
				if ( usr != null ) {
					usr.UpdateLastActivityDate ( );
					UserService.Update ( usr );
				}
			}

			return user;
		}

		/// <summary>
		/// 	Gets information from the data source for a user. Provides an option to update the last-activity date/time stamp for the user.
		/// </summary>
		/// <param name = "username">The name of the user to get information for.</param>
		/// <param name = "userIsOnline">true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user.</param>
		/// <returns>
		/// 	A <see cref = "T:System.Web.Security.MembershipUser" /> object populated with the specified user's information from the data source.
		/// </returns>
		public override MembershipUser GetUser ( string username , bool userIsOnline ) {
			MembershipUser user = null;
			User usr;

			if ( string.IsNullOrEmpty ( username ) ) {
				throw new ArgumentNullException ( "username" );
			}

			try {
				usr = UserService.GetUserByUsername ( username );

				if ( usr != null ) {
					user = SecurityProviderHelper.ToMembershipUser ( Name , usr );
				}
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable to retrive the user" , exception );
			}

			if ( userIsOnline ) {
				if ( usr != null ) {
					usr.UpdateLastActivityDate ( );
					UserService.Update ( usr );
				}
			}

			return user;
		}

		/// <summary>
		/// 	Gets the user name associated with the specified e-mail address.
		/// </summary>
		/// <param name = "email">The e-mail address to search for.</param>
		/// <returns>
		/// 	The user name associated with the specified e-mail address. If no match is found, return null.
		/// </returns>
		public override string GetUserNameByEmail ( string email ) {
			if ( string.IsNullOrEmpty ( email ) ) {
				throw new ArgumentNullException ( "email" );
			}

			try {
				User usr = UserService.GetUserByEmail ( new MailAddress ( email ) );

				if ( usr != null ) {
					return usr.Username;
				}
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable to retrive the user" , exception );
			}

			return null;
		}

		/// <summary>
		/// 	Removes a user from the membership data source.
		/// </summary>
		/// <param name = "username">The name of the user to delete.</param>
		/// <param name = "deleteAllRelatedData">true to delete data related to the user from the database; false to leave data related to the user in the database.</param>
		/// <returns>
		/// 	true if the user was successfully deleted; otherwise, false.
		/// </returns>
		public override bool DeleteUser ( string username , bool deleteAllRelatedData ) {
			try {
				User user = UserService.GetUserByUsername ( username );

				if ( user == null ) {
					throw new ProviderException ( "Unable to retrive the user." );
				}

				UserService.Delete ( user );
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable to delete the user." , exception );
			}
			return true;
		}

		/// <summary>
		/// 	Gets a collection of all the users in the data source in pages of data.
		/// </summary>
		/// <param name = "pageIndex">The index of the page of results to return. <paramref name = "pageIndex" /> is zero-based.</param>
		/// <param name = "pageSize">The size of the page of results to return.</param>
		/// <param name = "totalRecords">The total number of matched users.</param>
		/// <returns>
		/// 	A <see cref = "T:System.Web.Security.MembershipUserCollection" /> collection that contains a page of <paramref name = "pageSize" /><see cref = "T:System.Web.Security.MembershipUser" /> objects beginning at the page specified by <paramref name = "pageIndex" />.
		/// </returns>
		public override MembershipUserCollection GetAllUsers ( int pageIndex , int pageSize , out int totalRecords ) {
			var users = new MembershipUserCollection ( );
			try {
				//The membership is page zero based, but out user service not.
				IPagedResult <User> items = UserService.GetAllUsers ( pageIndex , pageSize );

				foreach ( User user in items.Result ) {
					users.Add ( SecurityProviderHelper.ToMembershipUser ( Name , user ) );
				}

				totalRecords = ( int ) items.TotalCount;
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable to retrive all users." , exception );
			}

			return users;
		}

		/// <summary>
		/// 	Gets the number of users currently accessing the application.
		/// </summary>
		/// <returns>
		/// 	The number of users currently accessing the application.
		/// </returns>
		public override int GetNumberOfUsersOnline ( ) {
			try {
				return UserService.GetNumberOfUsersOnline ( Membership.UserIsOnlineTimeWindow );
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable to retrive online users number" , exception );
			}
		}

		/// <summary>
		/// 	Gets a collection of membership users where the user name contains the specified user name to match.
		/// </summary>
		/// <param name = "usernameToMatch">The user name to search for.</param>
		/// <param name = "pageIndex">The index of the page of results to return. <paramref name = "pageIndex" /> is zero-based.</param>
		/// <param name = "pageSize">The size of the page of results to return.</param>
		/// <param name = "totalRecords">The total number of matched users.</param>
		/// <returns>
		/// 	A <see cref = "T:System.Web.Security.MembershipUserCollection" /> collection that contains a page of <paramref name = "pageSize" /><see cref = "T:System.Web.Security.MembershipUser" /> objects beginning at the page specified by <paramref name = "pageIndex" />.
		/// </returns>
		public override MembershipUserCollection FindUsersByName ( string usernameToMatch , int pageIndex , int pageSize , out int totalRecords ) {
			var users = new MembershipUserCollection ( );
			try {
				usernameToMatch = usernameToMatch.Replace ( '*' , '%' );
				usernameToMatch = usernameToMatch.Replace ( '?' , '_' );

				IPagedResult <User> items = UserService.FindUsersByName ( usernameToMatch , pageIndex , pageSize );

				foreach ( User user in items.Result ) {
					users.Add ( SecurityProviderHelper.ToMembershipUser ( Name , user ) );
				}

				totalRecords = ( int ) items.TotalCount;
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable to retrive the users" , exception );
			}

			return users;
		}

		/// <summary>
		/// 	Gets a collection of membership users where the e-mail address contains the specified e-mail address to match.
		/// </summary>
		/// <param name = "emailToMatch">The e-mail address to search for.</param>
		/// <param name = "pageIndex">The index of the page of results to return. <paramref name = "pageIndex" /> is zero-based.</param>
		/// <param name = "pageSize">The size of the page of results to return.</param>
		/// <param name = "totalRecords">The total number of matched users.</param>
		/// <returns>
		/// 	A <see cref = "T:System.Web.Security.MembershipUserCollection" /> collection that contains a page of <paramref name = "pageSize" /><see cref = "T:System.Web.Security.MembershipUser" /> objects beginning at the page specified by <paramref name = "pageIndex" />.
		/// </returns>
		public override MembershipUserCollection FindUsersByEmail ( string emailToMatch , int pageIndex , int pageSize , out int totalRecords ) {
			var users = new MembershipUserCollection ( );
			try {
				emailToMatch = emailToMatch.Replace ( '*' , '%' );
				emailToMatch = emailToMatch.Replace ( '?' , '_' );

				IPagedResult <User> items = UserService.FindUsersByEmail ( emailToMatch , pageIndex , pageSize );

				foreach ( User user in items.Result ) {
					users.Add ( SecurityProviderHelper.ToMembershipUser ( Name , user ) );
				}

				totalRecords = ( int ) items.TotalCount;
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable to retrive the users" , exception );
			}
			return users;
		}
	}
}
